<!DOCTYPE html>
<html lang="en"><head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>FlipBook3D with Three.js MOD3.js and Tween.js</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
	<style>
	body
	{
	 background-color:#121212;
	 margin:0;
	 padding:0;
	}
	</style>
	<script src="three.js/build/three.min.js"></script>
	<script src="vendor/three.js/Detector.js"></script>
	<script src="three.js/examples/js/libs/stats.min.js"></script>
	<script src="js/vendor/dat.gui.min.js"></script>
	
	<script src="vendor/threex/THREEx.FullScreen.js"></script>
	<script src="vendor/threex/THREEx.WindowResize.js"></script>

    <script src="MOD3/build/MOD3.js"></script>

	<script type="text/javascript" src="js/shaders/BendShader.js"></script>
	<script src="three.js/examples/js/controls/TrackballControls.js"></script>
	<script type="text/javascript" src="https://raw.github.com/sole/tween.js/master/build/tween.min.js"></script>
	<script type="text/javascript" src="js/RequestAnimationFrame.js"></script>
	<script type="text/javascript" src="js/FlipBook3D.js"></script>
	</head>
	<body>
		<div id="container" style="position:relative;margin:0 auto;padding:0;z-index:0;cursor:pointer;"></div>

<script>
		/*
 * Cloth Simulation using a relaxed constrains solver
 */

// Suggested Readings

// Advanced Character Physics by Thomas Jakobsen Character
// http://freespace.virgin.net/hugo.elias/models/m_cloth.htm
// http://en.wikipedia.org/wiki/Cloth_modeling
// http://cg.alexandra.dk/tag/spring-mass-system/
// Real-time Cloth Animation http://www.darwin3d.com/gamedev/articles/col0599.pdf

var DAMPING = 0.03;
var DRAG = 1 - DAMPING;
var MASS = .1;
var restDistance = 25;


var xSegs = 10; //
var ySegs = 10; //

var clothFunction = plane(restDistance * xSegs, restDistance * ySegs);

var cloth = new Cloth(xSegs, ySegs);

var GRAVITY = 981 * 1.4; // 
var gravity = new THREE.Vector3( 0, -GRAVITY, 0 ).multiplyScalar(MASS);


var TIMESTEP = 18 / 1000;
var TIMESTEP_SQ = TIMESTEP * TIMESTEP;

var pins = [];


var wind = true;
var windStrength = 2;
var windForce = new THREE.Vector3(0,0,0);

var ballPosition = new THREE.Vector3(0, -45, 0);
var ballSize = 60; //40

var tmpForce = new THREE.Vector3();

var lastTime;


function plane(width, height) {

	return function(u, v) {
		var x = (u-0.5) * width;
		var y = (v+0.5) * height;
		var z = 0;

		return new THREE.Vector3(x, y, z);
	};
}

function Particle(x, y, z, mass) {
	this.position = clothFunction(x, y); // position
	this.previous = clothFunction(x, y); // previous
	this.original = clothFunction(x, y); 
	this.a = new THREE.Vector3(0, 0, 0); // acceleration
	this.mass = mass;
	this.invMass = 1 / mass;
	this.tmp = new THREE.Vector3();
	this.tmp2 = new THREE.Vector3();
}

// Force -> Acceleration
Particle.prototype.addForce = function(force) {
	this.a.addSelf(
		this.tmp2.copy(force).multiplyScalar(this.invMass)
	);
};


// Performs verlet integration
Particle.prototype.integrate = function(timesq) {
	var newPos = this.tmp.sub(this.position, this.previous);
	newPos.multiplyScalar(DRAG).addSelf(this.position);
	newPos.addSelf(this.a.multiplyScalar(timesq));
	
	this.tmp = this.previous;
	this.previous = this.position;
	this.position = newPos;

	this.a.set(0, 0, 0);
}


var diff = new THREE.Vector3();

function satisifyConstrains(p1, p2, distance) {
	diff.sub(p2.position, p1.position);
	var currentDist = diff.length();
	if (currentDist==0) return; // prevents division by 0
	var correction = diff.multiplyScalar(1 - distance/currentDist);
	var correctionHalf = correction.multiplyScalar(0.5);
	p1.position.addSelf(correctionHalf);
	p2.position.subSelf(correctionHalf);
}


function Cloth(w, h) {
	w = w || 10;
	h = h || 10;
	this.w = w;
	this.h = h;

	var particles = [];
	var constrains = [];

	var u, v;

	// Create particles
	for (v=0;v<=h;v++) {
		for (u=0;u<=w;u++) {
			particles.push(
				new Particle(u/w, v/h, 0, MASS)
			);
		}
	}

	// Structural

	for (v=0;v<h;v++) {
		for (u=0;u<w;u++) {

			constrains.push([
				particles[index(u, v)],
				particles[index(u, v+1)],
				restDistance
			]);

			constrains.push([
				particles[index(u, v)],
				particles[index(u+1, v)],
				restDistance
			]);

		}
	}

	for (u=w, v=0;v<h;v++) {
		constrains.push([
			particles[index(u, v)],
			particles[index(u, v+1)],
			restDistance

		]);
	}

	for (v=h, u=0;u<w;u++) {
		constrains.push([
			particles[index(u, v)],
			particles[index(u+1, v)],
			restDistance
		]);
	}


	// While many system uses shear and bend springs,
	// the relax constrains model seem to be just fine
	// using structural springs.
	// Shear
	// var diagonalDist = Math.sqrt(restDistance * restDistance * 2);


	// for (v=0;v<h;v++) {
	// 	for (u=0;u<w;u++) {

	// 		constrains.push([
	// 			particles[index(u, v)],
	// 			particles[index(u+1, v+1)],
	// 			diagonalDist
	// 		]);

	// 		constrains.push([
	// 			particles[index(u+1, v)],
	// 			particles[index(u, v+1)],
	// 			diagonalDist
	// 		]);

	// 	}
	// }


	this.particles = particles;
	this.constrains = constrains;

	function index(u, v) {
		return u + v * (w + 1);
	}

	this.index = index;

}

function simulate(time) {
	if (!lastTime) {
		lastTime = time;
		return;
	}
	
	var i, il, particles, particle, pt, constrains, constrain;

	// Aerodynamics forces
	if (wind) {
		var face, faces = clothGeometry.faces, normal;

		particles = cloth.particles;

		for (i=0,il=faces.length;i<il;i++) {
			face = faces[i];
			normal = face.normal;

			tmpForce.copy(normal).normalize().multiplyScalar(normal.dot(windForce));
			particles[face.a].addForce(tmpForce);
			particles[face.b].addForce(tmpForce);
			particles[face.c].addForce(tmpForce);
		}
	}
	
	for (particles = cloth.particles, i=0, il = particles.length
			;i<il;i++) {
		particle = particles[i];
		particle.addForce(gravity);

		particle.integrate(TIMESTEP_SQ);
	}

	// Start Constrains

	constrains = cloth.constrains,
	il = constrains.length;
	for (i=0;i<il;i++) {
		constrain = constrains[i];
		satisifyConstrains(constrain[0], constrain[1], constrain[2]);
	}

	// Ball Constrains


	ballPosition.z = -Math.sin(Date.now()/600) * 90 ; //+ 40;
	ballPosition.x = Math.cos(Date.now()/400) * 70

	if (0 && sphere.visible)
	for (particles = cloth.particles, i=0, il = particles.length
			;i<il;i++) {
		particle = particles[i];
		pos = particle.position;
		diff.sub(pos, ballPosition);
		if (diff.length() < ballSize) {
			// collided
			diff.normalize().multiplyScalar(ballSize);
			pos.copy(ballPosition).addSelf(diff);
		}
	}
	
//&& controller.current.object==plane
	if(controller.current ) {
		//console.log(controller.current);
		for (particles = cloth.particles, i=0, il = particles.length
				;i<il;i++) {
			particle = particles[i];
			pos = particle.position;
			diff.sub(pos, controller.current.point);
			if (diff.length() < 5) {
				// collided
				diff.normalize().multiplyScalar(5);
				var p = controller.current.point.clone();
				p.z = 20;
				pos.copy(p).addSelf(diff);
			}
		}
	}

	// Floor Constains
	for (particles = cloth.particles, i=0, il = particles.length
			;i<il;i++) {
		particle = particles[i];
		pos = particle.position;
		if (pos.y < -250) {
			pos.y = -250;
		}
	}

	// Pin Constrains
	for (i=0, il=pins.length;i<il;i++) {
		var xy = pins[i];
		var p = particles[xy];
		p.position.copy(p.original);
		p.previous.copy(p.original);
	}


}
</script>
		
		
<script id="vertexShader" type="x-shader">
#ifdef GL_ES

precision highp float;

#endif

//attribute vec3 position;
varying vec2 vUv;
varying vec2 vUv2;
	varying vec2 vTextureCoord;
	varying vec3 vNormal;

uniform vec2 bounds;
uniform float bendOffset;
uniform float bendAngle;
uniform float bendAxisAngle;

const float PI = 3.14285714286;

vec3 bend(vec3 ip, float ba, vec2 b, float o, float a) {
	vec3 op = ip;
	ip.x = op.x * cos(a) - op.y * sin(a);
	ip.y = op.x * sin(a) + op.y * cos(a);
	if(ba != 0.0) {
		float radius = b.y / ba;
		float onp = (ip.x - b.x) / b.y - o;
		ip.z = cos(onp * ba) * radius - radius;
		ip.x = (b.x + b.y * o) + sin(onp * ba) * radius;
	}
	op = ip;
	ip.x = op.x * cos(-a) - op.y * sin(-a);
	ip.y = op.x * sin(-a) + op.y * cos(-a);
	return ip;
}

void main(void) {		
	vUv = uv;
	//vUv2 = uv2;
	//vTextureCoord = getTextureCoord(aTextureCoord);	
	    //vNormal = nMatrix * aVertexNormal;
	vNormal = normalize( ( modelViewMatrix * vec4( normal, 0.0 ) ).xyz );
	
	vec3 p = bend(position, bendAxisAngle, bounds, bendOffset, bendAngle);
    gl_Position = projectionMatrix * modelViewMatrix * vec4( p, 1.0 );
}
</script>

<script id="fragmentShader" type="x-shader">
precision mediump float;
precision mediump sampler2D;

varying vec2 vUv;
varying vec2 vUv2;
	varying vec2 vTextureCoord;
	varying vec3 vNormal;

uniform sampler2D frame;
uniform vec2 size;
uniform float amount;

float PI = 3.141592;
float radius = 1.0 / PI / 2.0;

float scale = 512.0;

float sharpness = 3.0;

float cylinderCenter(){
	return amount;
}

float cylinderAngle(){
	return radians(360.0 * amount);
}

vec4 antiAlias(vec4 color1, vec4 color2, float distance)
{
	distance *= scale;
	if (distance < 0.0) return color2;
	if (distance > 2.0) return color1;
	float dd = pow(1.0 - distance / 2.0, sharpness);
	return ((color2 - color1) * dd) + color1;
}

float distanceToEdge(vec3 point){
	float dx = abs(point.x > 0.5 ? 1.0 - point.x : point.x);
	float dy = abs(point.y > 0.5 ? 1.0 - point.y : point.y);
	if (point.x < 0.0) dx = -point.x;
	if (point.x > 1.0) dx = point.x - 1.0;
	if (point.y < 0.0) dy = -point.y;
	if (point.y > 1.0) dy = point.y - 1.0;
	if ((point.x < 0.0 || point.x > 1.0) && (point.y < 0.0 || point.y > 1.0)) return sqrt(dx * dx + dy * dy);
	return min(dx, dy);
}

vec4 seeThrough(float yc, vec2 p, mat3 rotation, mat3 rrotation){
	float hitAngle = mod(180.0 - degrees(acos(yc / radius) - cylinderAngle()), 360.0);
	float hitPoint = hitAngle / 360.0;
	vec3 point = rotation * vec3(p, 1.0);
	point.y = hitPoint;
	point = rrotation * point;
	
	if (yc <= 0.0 && (point.x < 0.0 || point.y < 0.0 || point.x > 1.0 || point.y > 1.0)){
		return vec4(0.0, 0.0, 0.0, 0.0);
	}
	
	if (yc > 0.0) return texture2D(frame, p);
	
	vec4 color = texture2D(frame, vec2(point.x, point.y));
	vec4 tcolor = vec4(0.0, 0.0, 0.0, 0.0);

	return antiAlias(color, tcolor, distanceToEdge(point));
}

vec4 seeThroughWithShadow(float yc, vec2 p, vec3 point, mat3 rotation, mat3 rrotation){
	float shadow = distanceToEdge(point) * 30.0;
	shadow = (1.0 - shadow) / 3.0;
	if (shadow < 0.0) shadow = 0.0;
	else shadow *= amount;

	vec4 shadowColor = seeThrough(yc, p, rotation, rrotation);
	shadowColor.r -= shadow;
	shadowColor.g -= shadow;
	shadowColor.b -= shadow;
	return shadowColor;
}

vec4 backside(float yc, vec3 point){
	vec4 color = texture2D(frame, vec2(point.x, point.y));
	float gray = (color.r + color.b + color.g) / 15.0;
	gray += (8.0 / 10.0) * (pow(1.0 - abs(yc / radius), 2.0 / 10.0) / 2.0 + (5.0 / 10.0));
	color.r = gray;
	color.g = gray;
	color.b = gray;
	return color;
}

vec4 behindSurface(float yc, vec3 point, mat3 rrotation){
	float shado = (1.0 - ((-radius - yc) / amount * 7.0)) / 6.0;
	shado *= 1.0 - abs(point.x - 5.0 / 10.0);
	
	yc = (-radius - radius - yc);
	
	float hitAngle = mod(degrees(acos(yc / radius) + cylinderAngle()) - 180.0, 360.0);			

	float hitPoint = hitAngle / 360.0;
	
	point.y = hitPoint;
	
	point = rrotation * point;
	
	if (yc < 0.0 && point.x >= 0.0 && point.y >= 0.0 && point.x <= 1.0 && point.y <= 1.0 && (hitAngle < 180.0 || amount > 5.0 / 10.0)){
		shado = 1.0 - (sqrt(pow(point.x - (5.0 / 10.0), 2.0) + pow(point.y - (5.0 / 10.0), 2.0)) / (71.0 / 100.0));
		shado *= pow(-yc / radius, 3.0);
		shado *= 5.0 / 10.0;
	} else
		shado = 0.0;
	
	return vec4(0.0, 0.0, 0.0, shado);
}

void main(void) {
	vec2 position1 = .5 * vUv;
	gl_FragColor = texture2D(frame, position1);
	return;
		
	float angle = 30.0;
	float c = cos(radians(-angle));
	float s = sin(radians(-angle));

	mat3 rotation = mat3(
		c, s, 0,
		-s, c, 0,
		0.12, 0.258, 1
	);

	c = cos(radians(angle));
	s = sin(radians(angle));

	mat3 rrotation = mat3(
		c, s, 0,
		-s, c, 0,
		0.15, -0.5, 1
	);

	vec2 p = vec2(gl_FragCoord.x / size.x, gl_FragCoord.y / size.y);
	vec3 point = rotation * vec3(p, 1.0);
	float y = point.y;
	
	float yc = y - cylinderCenter();
	
	if (yc < -radius)
	{
		// Behind surface
		gl_FragColor = behindSurface(yc, point, rrotation);
		return;
	}

	if (yc > radius)
	{
		// Flat surface
		gl_FragColor = texture2D(frame, p);
		return;
	}
	
	float hitAngle = mod(degrees(acos(yc / radius) + cylinderAngle()) - 180.0, 360.0);			

	float hitPoint = hitAngle / 360.0;
	
	point.y = hitPoint;
	
	point = rrotation * point;


	if ((hitAngle > 180.0 && amount < 5.0 / 10.0) || (hitAngle > 90.0 && amount < 0.0))
	{
		gl_FragColor = seeThrough(yc, p, rotation, rrotation);
		return;
	}

	if (point.x < 0.0 || point.y < 0.0 || point.x > 1.0 || point.y > 1.0){
		gl_FragColor = seeThroughWithShadow(yc, p, point, rotation, rrotation);
		return;
	}
	
	vec4 color = backside(yc, point);
	
	vec4 otherColor;
	if (yc < 0.0){
		
		float shado = 1.0 - (sqrt(pow(point.x - 0.5, 2.0) + pow(point.y - 0.5, 2.0)) / 0.71);
		shado *= pow(-yc / radius, 3.0);
		shado *= 0.5;

		otherColor = vec4(0.0, 0.0, 0.0, shado);
	} else {
		otherColor = texture2D(frame, p);
	}
	
	color = antiAlias(color, otherColor, radius - abs(yc));

	//gl_FragColor = antiAlias(color, vec4(1.0, 0.0, 0.0, 1.0), 3.0);

	//gl_FragColor = antiAlias(color, seeThrough(yc, p, rotation, rrotation), 3.0);
	
	vec4 cl = seeThroughWithShadow(yc, p, point, rotation, rrotation);
	float dist = distanceToEdge(point);
	
	gl_FragColor = antiAlias(color, cl, dist);
	
	//gl_FragColor = antiAlias(gl_FragColor, texture2D(frame, p), radius - yc);
	//gl_FragColor = antiAlias(gl_FragColor, vec4(0.0, 0.0, 0.0, 0.0), radius + yc);

}
</script>
		
        <script>
		/* testing cloth simulation */

			var pinsFormation = [];
			var pins = [6];

			pinsFormation.push( pins );

			pins = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
			pinsFormation.push( pins );

			pins = [ 0 ];
			pinsFormation.push( pins );

			pins = []; // cut the rope ;)
			pinsFormation.push( pins );

			pins = [ 0, cloth.w ]; // classic 2 pins
			pinsFormation.push( pins );

			pins = pinsFormation[ 1 ];


			function togglePins() {

				pins = pinsFormation[ ~~( Math.random() * pinsFormation.length ) ];

			}	
		
		
			var Properties = function() {
				this.bendAngle = 0.0;
				this.bendOffset = 0.0;
				this.bendAxisAngle = 0.0;
				this.boundX = -100;
				this.boundY = 100;
				this.flipLeft = function () { book.pages[book.pages.length - book.flippedright].flipLeft() };
				this.flipRight = function () { book.pages[book.flippedleft - 1].flipRight() };
			}
			var properties, gui, stats;			
            var container;
            var camera, scene, renderer, projector, controller;

            var w, h, w2, h2;
            var images = [{ f: "img/catalog_01.jpg", b: "img/catalog_04.jpg", hard: 1 },
            { f: "img/catalog_05.jpg", b: "img/catalog_06.jpg", hard: 0.5 },
            { f: "img/catalog_07.jpg", b: "img/catalog_08.jpg", hard: 0 },
            { f: "img/catalog_09.jpg", b: "img/catalog_11.jpg", hard: 0 },
            /*{f:"img/catalog_11.jpg" ,b:"img/catalog_12.jpg", hard:0},
            {f:"img/catalog_09.jpg", b:"img/catalog_07.jpg", hard:0},
            {f:"img/catalog_06.jpg", b:"img/catalog_01.jpg", hard:0},*/
             {f: "img/catalog_12.jpg", b: "img/catalog_13.jpg", hard: 1}];
            var book;
            var pagew = 300;
            var pageh = pagew * 10 / 7;

			gui();
            if( !init() )	
				animate();

			function gui() {
				properties = new Properties();
				
				gui = new dat.GUI();
				gui.add(properties, 'bendAngle').min(Math.PI * -2).max(Math.PI * 2).listen();
				gui.add(properties, 'bendOffset').min(-1).max(1);
				gui.add(properties, 'bendAxisAngle').min(Math.PI * -2.5).max(Math.PI * 2.5);
				gui.add(properties, 'boundX').min(-400).max(400);
				gui.add(properties, 'boundY').min(-400).max(400);
				gui.add(properties, 'flipLeft');
				gui.add(properties, 'flipRight');
				
				stats = new Stats();
				stats.domElement.style.position = 'absolute';
				stats.domElement.style.top = '0px';
				document.body.appendChild( stats.domElement );
			}

            function init() {				
				if( Detector.webgl ){
					renderer = new THREE.WebGLRenderer({
						antialias		: true,	// to get smoother output
						preserveDrawingBuffer	: true	// to allow screenshot
					});
					renderer.setClearColorHex( 0xBBBBBB, 1 );	
					renderer.clear();
				// uncomment if webgl is required
				//}else{
				//	Detector.addGetWebGLMessage();
				//	return true;
				}else{
					renderer	= new THREE.CanvasRenderer();
				}
				
				container = document.getElementById('container');
                w = window.innerWidth;
                h = window.innerHeight;
                w2 = w / 2;
                h2 = h / 2;
				
				//////////////////////////////////////////////////////////////////////
				// RENDERER
				//////////////////////////////////////////////////////////////////////
				
				// webgl renderer gives better rendering without problems
				//renderer = new THREE.WebGLRenderer({ antialias__: true, alpha__: false, clearColor__: 0xfafafa, clearAlpha__: 1 });
				renderer.setSize(w, h);
				renderer.shadowMapEnabled = true;
				
				//renderer.shadowCameraFov = 50;
				//renderer.shadowMapWidth = 1024;;
				//renderer.shadowMapHeight = 1024;
				//renderer.shadowMapSoft = true;

				//renderer.setClearColor( scene.fog.color, 1 );

				//renderer.gammaInput = true;
				//renderer.gammaOutput = true;
				//renderer.physicallyBasedShading = true;
					
				container.appendChild(renderer.domElement);			
            

				//////////////////////////////////////////////////////////////////////
				// SCENE
				//////////////////////////////////////////////////////////////////////
				
                scene = new THREE.Scene();
                //scene.fog = new THREE.Fog( 0xfafafa, 1000, 3000 );
                //scene.fog.color.setHSV( 0.6, 0.125, 1 );				

                var object = new THREE.AxisHelper(500);
                object.position.set(0, 0, 0);
                scene.add(object);

	
				controller = new THREE.Object3D();
				controller.objects = [];
				controller.scene = scene;
				controller.gui = gui;
				controller.color = 0xFFFFFF;
				controller.createNew = function() {
					var cube = new THREE.Mesh(
					  new THREE.CubeGeometry(20,20,20),
					  new THREE.MeshPhongMaterial({color: 0xFFFFFF})
					);
					cube.castShadow = cube.receiveShadow = true;
					this.scene.add(cube);
					this.objects.push(cube);
					this.setCurrent(cube);
				};
				controller.setCurrent = function(current) {
					if (this.current)
					  this.current.object.material.ambient.setHex(0x000000);
					this.current = current;
					if (this.current) {
						console.log(this.current);
					  this.current.object.material.ambient.setHex(0x888800);
					  this.position.x = current.object.position.x;
					  this.position.y = current.object.position.y;
					  this.position.z = current.object.position.z;
					  this.position.x = current.object.scale.x;
					  this.position.y = current.object.scale.y;
					  this.position.z = current.object.scale.z;
					}
				  };			

			
	  
				window.addEventListener('mousedown', function (ev){
				  if (ev.target == renderer.domElement) {
					var width = renderer.domElement.width;
					var height = renderer.domElement.height;
					var x = ev.clientX;
					var y = ev.clientY;
					var v = new THREE.Vector3((x/width)*2-1, -(y/height)*2+1, 0.5);
					projector.unprojectVector(v, camera);
					var ray = new THREE.Ray(camera.position, 
											v.subSelf(camera.position).normalize());
					var intersects = ray.intersectObjects(controller.objects);
					if (intersects.length > 0) {
					  controller.setCurrent(intersects[0]);			  
					}
				  }
				}, true);
				
				//////////////////////////////////////////////////////////////////////
				// CAMERA
				//////////////////////////////////////////////////////////////////////
				
				// new THREE.PerspectiveCamera( FOV, viewAspectRatio, zNear, zFar );
                camera = new THREE.PerspectiveCamera(45, w / h, 1, 10000);
				camera.position.set(0, 0, 600);
                scene.add(camera);

                controls = new THREE.TrackballControls(camera, container);

                projector = new THREE.Projector();

				/////////////////////////////////////////////////////////////////////////////
                // LIGHTS
				/////////////////////////////////////////////////////////////////////////////
				
                var ambient = new THREE.AmbientLight(0xffffff);
                //scene.add(ambient);

				light = new THREE.SpotLight();
				light.position.set( 500, 100, 1000 );
				//light.target.position.set( 0, 0, 0 );

				light.castShadow = true;

				//light.shadowCameraNear = 100;
				//light.shadowCameraFar = 1500;
				//light.shadowCameraFov = 50;

				light.shadowCameraVisible = true;

				//light.shadowBias = 0.0001;
				//light.shadowDarkness = 0.5;

				//light.shadowMapWidth = 512;
				//light.shadowMapHeight = 512;

				scene.add( light );
				
                var directionalLight = new THREE.DirectionalLight( 0xffffff, 1.475 );
                directionalLight.position.set( 100, 100, 800 );
                //scene.add( directionalLight );


                var hemiLight = new THREE.HemisphereLight(0xffffff, 0xffffff, 1.25);
                //hemiLight.color.setHSV( 0.6, 0.45, 1 );
                //hemiLight.groundColor.setHSV( 0.1, 0.45, 0.95 );
                hemiLight.position.z = 500;
                //scene.add( hemiLight );
				

				//////////////////////////////////////////////////////////////////////
                // 
				//////////////////////////////////////////////////////////////////////

				var texture = THREE.ImageUtils.loadTexture( "three.js/examples/textures/2294472375_24a3b8ef46_o.jpg" );
                var planeMaterial = new THREE.MeshPhongMaterial({ color: 0xffdd99, 
					map: texture,
					side: THREE.DoubleSide });
				
				var shader = THREE.BendShader;
				uniforms = shader.uniforms;
				var vertexShader = shader.vertexShader;
				var fragmentShader = shader.fragmentShader;
				
				uniforms.colorTexture = { type: "t", value: THREE.ImageUtils.loadTexture( "three.js/examples/textures/2294472375_24a3b8ef46_o.jpg" ) };

				material = new THREE.ShaderMaterial( {
					uniforms: uniforms,
					//attributes: attributes,
					vertexShader: document.getElementById('vertexShader').textContent,
					fragmentShader: fragmentShader,
					//blending:       THREE.AdditiveBlending,
					//depthTest:      false,
					//wireframe: false,
					//lights: true,
					color: 0xffdd99, 
					map: texture,
					side: THREE.DoubleSide
				} );

				//var geometry = new THREE.PlaneGeometry(400, 300, 40, 30);
				clothGeometry = new THREE.ParametricGeometry( clothFunction, cloth.w, cloth.h, true );
				clothGeometry.dynamic = true;
				clothGeometry.computeFaceNormals();
				
                plane = new THREE.Mesh(clothGeometry, planeMaterial);
				
                plane.position.set(0, 0, 0);
                			
                plane.receiveShadow = true;	
				plane.castShadow = true;
                scene.add(plane);
				
				controller.objects.push(plane);
				
				// GROUND
				var planeMat = new THREE.MeshLambertMaterial({color: 0xFFFFFF});
				
                var texture1 = THREE.ImageUtils.loadTexture( "three.js/examples/textures/crate.gif" );
				texture1.anisotropy = 16;
				texture1.wrapS = texture1.wrapT = THREE.RepeatWrapping;
				texture1.repeat.set( 512, 512 );
				
                var planeMaterial2 = new THREE.MeshLambertMaterial({ color: 0xFFFFFF, map: texture1 });
				
                //THREE.ColorUtils.adjustHSV(planeMaterial.color, 0, 0, 0.9);
                //planeMaterial.ambient = planeMaterial.color;
				var geometry1 = new THREE.PlaneGeometry(400, 300, 10, 10);
				
				var ground = new THREE.Mesh(geometry1, planeMat);
				ground.position.set(0, 0, -30);
			
                ground.scale.set(10, 10, 10);
                ground.receiveShadow = true;
				scene.add(ground);	
				controller.objects.push(ground);	

	cube = new THREE.Mesh(
        new THREE.CubeGeometry(50,50,50),
        new THREE.MeshLambertMaterial({color: 0xff0000, shading: THREE.FlatShading})
      );
	  cube.position.z = 0;
      scene.add(cube);
				controller.objects.push(cube);
      cube.castShadow = true;
      cube.receiveShadow = true;				
				
				/////////////////////////////////////////////////////////////////////
				// BOOK
				/////////////////////////////////////////////////////////////////////

                book = new FlipBook3D.Book();
                book.pageWidth = pagew;
                book.pageHeight = pageh;
                //scene.add(book);
                for (var i = 0; i < images.length; i++) {
                    var texturefront = THREE.ImageUtils.loadTexture(images[i].f);
                    var textureback = THREE.ImageUtils.loadTexture(images[i].b);
					
                    book.addPage(texturefront, textureback, images[i].hard);
                }
				
				window.addEventListener( 'resize', onWindowResize, false );
				
				/*
                angle.addEventListener('change', function (event) {
                    var val = this.value; book.pages.forEach(function (element, index, array) {
                        console.log('angle:', val);
						material.uniforms.bendAngle.value = val;
                        element.bend.setAngle(val);
                        element.mod.apply();
                    })
                });
                offset.addEventListener('change', function (event) {
                    var val = this.value; book.pages.forEach(function (element, index, array) {
                        console.log('offset:', val);
						material.uniforms.offset.value = val;
                        element.bend.offset = val;
                        element.mod.apply();
                    })
                });
                force.addEventListener('change', function (event) {
                    var val = this.value; book.pages.forEach(function (element, index, array) {
                        console.log('force:', val);
						material.uniforms.bendAxisAngle.value = val;
                        element.bend.force = val;
                        element.mod.apply();
                    })
                });
				*/
            }
			
			function onWindowResize() {

				camera.aspect = window.innerWidth / window.innerHeight;
				camera.updateProjectionMatrix();

				renderer.setSize( window.innerWidth, window.innerHeight );
			}
			
            function animate() {		
				
				var time = Date.now();
				
                requestAnimationFrame(animate);
				simulate(time);
                render();
				stats.update();
            }

            function render() {
				
				material.uniforms.bendAngle.value 		= properties.bendAngle;
				material.uniforms.bendAxisAngle.value 	= properties.bendAxisAngle;
				material.uniforms.bendOffset.value 		= properties.bendOffset;
				material.uniforms.bounds.value 			= new THREE.Vector2( properties.boundX, properties.boundY );
				
                TWEEN.update();
                controls.update();				
				
				var p = cloth.particles;

				for ( var i = 0, il = p.length; i < il; i ++ ) {

					clothGeometry.vertices[ i ].copy( p[ i ].position );

				}

				clothGeometry.computeFaceNormals();
				clothGeometry.computeVertexNormals();

				clothGeometry.normalsNeedUpdate = true;
				clothGeometry.verticesNeedUpdate = true;
				
                renderer.render(scene, camera);				
            }
		</script>
</body></html>